﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Printing;
using System.Windows.Shapes;
using System.Windows.Navigation;

namespace OpenRPOSBackOffice
{
    public partial class TimeSheet : Page
    {
        DateTime dtStart = DateTime.Now.WeekStart(Globals.k_dow_week_start), dtLoaded;
        DateTime dtLastClick = System.DateTime.Now;
        public ObservableCollection<TimeSheetData> timeSheetDataSource = new ObservableCollection<TimeSheetData>();
        public bool bPunchedIn = false;

        public TimeSheet()
        {
            InitializeComponent();
            System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
            dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1000); // 1 second
            dispatcherTimer.Tick += new EventHandler(secondTick);
            dispatcherTimer.Start();
        }

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            init();
        }

        public void secondTick(object o, EventArgs sender)
        {
            lblCurrentTime.Content = DateTime.Now.ToString("h:mm:ss tt");
        }

        // Executes when the user navigates to this page.
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private void updatePunchStatus()
        {
            // Determine the login status of the current employee
            IEnumerable<ServiceReferenceOpenRPOS.TimeSheet> query = Globals.db.TimeSheetRecords.Where(x => x.col_EmployeeID == Globals.currentEmployee.col_ID &&
                x.col_PunchDateTime.Date == DateTime.Now.Date && x.col_Adjusted == false);
            bPunchedIn = false;
            if (query.Count() > 0)
            {
                if (query.Last().col_PunchType)
                {
                    lblPunchStatus.Content = "Status - In";
                    btnPunch.Content = "Punch Out";
                    lblRole.Content = "Role - " + Globals.db.RoleRecords.Where(x => x.col_ID == query.Last().col_RoleID);
                    bPunchedIn = true;
                }
            }
            if (!bPunchedIn)
            {
                lblPunchStatus.Content = "Status - Out";
                btnPunch.Content = "Punch In";
                lblRole.Content = "";
            }
        }

        // Executes when the user navigates to this page.
        public async void init()
        {
            Globals.showWaitCursor(true);
            dtLoaded = DateTime.MinValue;

            // Only allow roles to be selected by managers...
            cmbRoles.Visibility = (Globals.currentLogin.col_Manager) ? Visibility.Visible : Visibility.Collapsed;
            lblRole.Visibility = cmbRoles.Visibility;

            // Setup the initial period...
            dpPeriodStart.SelectedDate = dtStart;

            // Load relevant data...
            await Globals.db.GetEmployeeObjects("Active=1 order by LastName, FirstName, MiddleInitial");
            await Globals.db.GetRoleObjects("Active=1 order by Title");
            await Globals.db.GetEmployeeRoleObjects("Active=1");

            cmbRoles.Items.Clear();
            cmbRoles.Items.Add("All");
            foreach (ServiceReferenceOpenRPOS.Role role in Globals.db.RoleRecords)
            {
                cmbRoles.Items.Add(role.col_Title);
            }
            cmbRoles.SelectedIndex = 0;
            updatePunchStatus();
            Globals.timeSheet = this;
            Globals.showWaitCursor(false);
        }

        private string getEmployeeWeekHoursTotal(int employeeID, DateTime timeSheetDate, bool showPreference)
        {
            string employeeHours = "";
            TimeSpan ts = new TimeSpan();
            DateTime dtInPunch = DateTime.MinValue;

            // Sum the shift totals for the week
            IEnumerable<ServiceReferenceOpenRPOS.TimeSheet> query = Globals.db.TimeSheetRecords.Where(x => x.col_EmployeeID == employeeID &&
                x.col_PunchDateTime.Date >= timeSheetDate.Date && x.col_PunchDateTime.Date < timeSheetDate.Date.AddDays(Globals.k_int_days_in_week));
            foreach (ServiceReferenceOpenRPOS.TimeSheet timeSheetRecord in query)
            {
                if (timeSheetRecord.col_PunchType == true) // In-punch
                    dtInPunch = timeSheetRecord.col_PunchDateTime;
                else if (timeSheetRecord.col_PunchDateTime > dtInPunch)  // Out punch
                {
                    ts += timeSheetRecord.col_PunchDateTime - dtInPunch;
                    dtInPunch = DateTime.MinValue;
                }
            }

            employeeHours = ts.TotalHours.ToString("F02");

            return employeeHours;
        }

        private string getEmployeeTimeSheetData(int employeeID, DateTime scheduleDate, bool showPreference)
        {
            string strReturn = "";
            TimeSpan ts = new TimeSpan();
            DateTime dtInPunch = DateTime.MaxValue;

            // Sum the shift totals for the week
            IEnumerable<ServiceReferenceOpenRPOS.TimeSheet> query = Globals.db.TimeSheetRecords.Where(x => x.col_EmployeeID == employeeID &&
                x.col_PunchDateTime.Date == scheduleDate.Date && x.col_Adjusted == false);
            foreach (ServiceReferenceOpenRPOS.TimeSheet timeSheetRecord in query)
            {
                if (timeSheetRecord.col_PunchType == true) // In-punch
                {
                    dtInPunch = timeSheetRecord.col_PunchDateTime;
                    // Display the role as well
                    foreach (ServiceReferenceOpenRPOS.Role role in Globals.db.RoleRecords)
                        if (role.col_ID == timeSheetRecord.col_RoleID)
                            strReturn += role.col_Title + "\n";
                    strReturn += "IN: " + dtInPunch.ToShortTimeString() + ((timeSheetRecord.col_Adjustment) ? " (A)" : "") + "\n";
                }
                else if (timeSheetRecord.col_PunchDateTime > dtInPunch)  // Out punch
                {
                    ts += timeSheetRecord.col_PunchDateTime - dtInPunch;
                    strReturn += "OUT: " + timeSheetRecord.col_PunchDateTime.ToShortTimeString() + ((timeSheetRecord.col_Adjustment) ? " (A)" : "") + "\n";
                    dtInPunch = DateTime.MaxValue;
                }
            }

            if (strReturn.Length > 0)
            {
                strReturn += "Hours: " + ts.TotalHours.ToString("F02");
            }
            return strReturn;
        }

        public async void initTimeSheetGrid(bool reload=false)
        {
            Globals.showWaitCursor(true);
            if (!dtStart.Equals(dtLoaded)||reload)
                await Globals.db.GetTimeSheetObjects("PunchDateTime between '" + dtStart.ToShortDateString() + "' and '" +
                    dtStart.AddDays(Globals.k_int_days_in_week).ToShortDateString() + "' order by PunchDateTime");
            dtLoaded = dtStart;
            timeSheetDataSource.Clear();
            dgTimeSheet.Columns[1].Header = "Hours";
            dgTimeSheet.Columns[2].Header = dtStart.DayOfWeek.ToString().Substring(0, 3) + " (" + dtStart.Month.ToString() + "/" + dtStart.Day.ToString() + ")";
            dgTimeSheet.Columns[2].Width = DataGridLength.Auto;
            dgTimeSheet.Columns[3].Header = dtStart.AddDays(1).DayOfWeek.ToString().Substring(0, 3) + " (" + dtStart.AddDays(1).Month.ToString() + "/" + dtStart.AddDays(1).Day.ToString() + ")";
            dgTimeSheet.Columns[3].Width = DataGridLength.Auto;
            dgTimeSheet.Columns[4].Header = dtStart.AddDays(2).DayOfWeek.ToString().Substring(0, 3) + " (" + dtStart.AddDays(2).Month.ToString() + "/" + dtStart.AddDays(2).Day.ToString() + ")";
            dgTimeSheet.Columns[4].Width = DataGridLength.Auto;
            dgTimeSheet.Columns[5].Header = dtStart.AddDays(3).DayOfWeek.ToString().Substring(0, 3) + " (" + dtStart.AddDays(3).Month.ToString() + "/" + dtStart.AddDays(3).Day.ToString() + ")";
            dgTimeSheet.Columns[5].Width = DataGridLength.Auto;
            dgTimeSheet.Columns[6].Header = dtStart.AddDays(4).DayOfWeek.ToString().Substring(0, 3) + " (" + dtStart.AddDays(4).Month.ToString() + "/" + dtStart.AddDays(4).Day.ToString() + ")";
            dgTimeSheet.Columns[6].Width = DataGridLength.Auto;
            dgTimeSheet.Columns[7].Header = dtStart.AddDays(5).DayOfWeek.ToString().Substring(0, 3) + " (" + dtStart.AddDays(5).Month.ToString() + "/" + dtStart.AddDays(5).Day.ToString() + ")";
            dgTimeSheet.Columns[7].Width = DataGridLength.Auto;
            dgTimeSheet.Columns[8].Header = dtStart.AddDays(6).DayOfWeek.ToString().Substring(0, 3) + " (" + dtStart.AddDays(6).Month.ToString() + "/" + dtStart.AddDays(6).Day.ToString() + ")";
            dgTimeSheet.Columns[8].Width = DataGridLength.Auto;

            foreach (ServiceReferenceOpenRPOS.Employee emp in Globals.db.EmployeeRecords)
            {
                // Only allow scheduling for employees with defined roles
                bool bShowTimeSheet = (cmbRoles.SelectedIndex == 0 && Globals.db.EmployeeRoleRecords.Any(x => x.col_EmployeeID == emp.col_ID));
                // Filter on roles, if requested
                if (cmbRoles.SelectedIndex > 0)
                    bShowTimeSheet = Globals.db.EmployeeRoleRecords.Any(x => x.col_RoleID == Globals.db.RoleRecords[cmbRoles.SelectedIndex - 1].col_ID && x.col_EmployeeID == emp.col_ID);
                if (!Globals.currentLogin.col_Manager)
                    bShowTimeSheet = (emp.col_ID == Globals.currentEmployee.col_ID);
                if (bShowTimeSheet)
                {
                    TimeSheetData tsd = new TimeSheetData()
                    {
                        Name = emp.col_LastName + ", " + emp.col_FirstName + " " + emp.col_MiddleInitial,
                        EmployeeID = emp.col_ID,
                        Hours = getEmployeeWeekHoursTotal(emp.col_ID, dtStart, false),
                        Day0 = getEmployeeTimeSheetData(emp.col_ID, dtStart, false),
                        Day1 = getEmployeeTimeSheetData(emp.col_ID, dtStart.AddDays(1), false),
                        Day2 = getEmployeeTimeSheetData(emp.col_ID, dtStart.AddDays(2), false),
                        Day3 = getEmployeeTimeSheetData(emp.col_ID, dtStart.AddDays(3), false),
                        Day4 = getEmployeeTimeSheetData(emp.col_ID, dtStart.AddDays(4), false),
                        Day5 = getEmployeeTimeSheetData(emp.col_ID, dtStart.AddDays(5), false),
                        Day6 = getEmployeeTimeSheetData(emp.col_ID, dtStart.AddDays(6), false),
                    };
                    timeSheetDataSource.Add(tsd);
                }
            }
            dgTimeSheet.ItemsSource = timeSheetDataSource;
            Globals.showWaitCursor(false);
        }

        private void btnNextPeriod_Click(object sender, RoutedEventArgs e)
        {
            dpPeriodStart.SelectedDate = dtStart.AddDays(Globals.k_int_days_in_week);
        }

        private void btnPrevPeriod_Click(object sender, RoutedEventArgs e)
        {
            dpPeriodStart.SelectedDate = dtStart.AddDays(-1 * Globals.k_int_days_in_week);
        }

        private void dpPeriodStart_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
        {
            // Load the new period
            dtStart = (DateTime)dpPeriodStart.SelectedDate;
            dtStart = dtStart.WeekStart(Globals.k_dow_week_start);
            if (dtStart != (DateTime)dpPeriodStart.SelectedDate)
                dpPeriodStart.SelectedDate = dtStart;
            else
                initTimeSheetGrid(true);
        }

        private void cmbRoles_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            initTimeSheetGrid();
        }

        private async void btnPunch_Click(object sender, RoutedEventArgs e)
        {
            if (bPunchedIn) // Prompt the user to punch-out
            {
                if (MessageBox.Show("Are you sure you want to punch out?", "Confirm", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
                {
                    // Punch the user out
                    Globals.showWaitCursor(true);
                    ServiceReferenceOpenRPOS.TimeSheet timeSheet = new ServiceReferenceOpenRPOS.TimeSheet();
                    timeSheet.bFilterPunchType = true;
                    timeSheet.col_PunchType = false; // Out-punch
                    timeSheet.bFilterEmployeeID = true;
                    timeSheet.col_EmployeeID = Globals.currentEmployee.col_ID;
                    timeSheet.bFilterPunchDateTime = true;
                    timeSheet.col_PunchDateTime = DateTime.Now;
                    btnPunch.Content = "Punch In";
                    bPunchedIn = false;
                    lblPunchStatus.Content = "Status - Out";
                    lblCurrentRole.Content = "";
                    await Globals.db.AddTimeSheetObject(timeSheet);
                    // Re-initialize the time sheet data
                    dpPeriodStart.SelectedDate = timeSheet.col_PunchDateTime;
                    Globals.mainPage.ContentFrame.Content = Globals.timeSheet;
                }
            }
            else // Prompt the user to punch-in
            {
                PunchInDialog pid = new PunchInDialog();
                pid.Show();
            }
        }

        private void dgTimeSheet_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Space)
            {
                doCellEdit();
            }
        }

        private void dgTimeSheet_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            DateTime dtCurrClick = DateTime.Now;
            // Test for "Double-Click"
            if (dtCurrClick.Ticks - dtLastClick.Ticks < Globals.k_long_max_ticks_between_clicks)
                doCellEdit();
            dtLastClick = dtCurrClick;
        }

        private void doCellEdit()
        {
            TimeSheetData tsd = (TimeSheetData)(dgTimeSheet.SelectedItem);
            if (Globals.currentLogin.col_Manager)
            {
                if (dgTimeSheet.CurrentColumn.DisplayIndex > 1)
                {
                    AdjustTimeSheetDialog atsd = new AdjustTimeSheetDialog();
                    Globals.showWaitCursor(true);
                    atsd.init(tsd.EmployeeID, dtStart.AddDays(dgTimeSheet.CurrentColumn.DisplayIndex - 2));
                    atsd.Show();
                }
                dgTimeSheet.SelectedItem = null;
            }
        }

    }

    public class TimeSheetData
    {
        public string Name { get; set; }
        public int EmployeeID { get; set; }
        public string Hours { get; set; }
        public string Day0 { get; set; }
        public string Day1 { get; set; }
        public string Day2 { get; set; }
        public string Day3 { get; set; }
        public string Day4 { get; set; }
        public string Day5 { get; set; }
        public string Day6 { get; set; }
    }
}
